﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.IO;

namespace ReactiveXLearning
{
    public partial class frmSample : frmBase
    {
        WebClient client = new WebClient() { Encoding = Encoding.UTF8 };
        IObservable<DownloadProgressChangedEventArgs> progress_change;
        IObservable<DownloadDataCompletedEventArgs> data_downloaded;
        IObservable<MouseEventArgs> mouse_move;
        IObservable<KeyEventArgs> url_keydown;
        IDisposable mouse_move_dis;
        IDisposable progress_change_dis;
        IDisposable data_download_dis;
        IDisposable url_keydown_dis;

        public frmSample()
        {
            InitializeComponent();
            Load += frmSample_Load;
            FormClosed += FrmSample_FormClosed;
        }

        private void frmSample_Load(object sender, EventArgs e)
        {
            //事件
            btn_copy.Click += Btn_copy_Click;

            //界面
            ProgressVisible(false);

            #region 处理事件

            mouse_move = Observable.FromEventPattern<MouseEventArgs>(this, "MouseMove")
                .Sample(TimeSpan.FromSeconds(.5)) //鼠标移动时，每 0.5 秒取一次事件
                .Select(h => h.EventArgs);

            progress_change = Observable.FromEventPattern<DownloadProgressChangedEventArgs>(client, "DownloadProgressChanged")
                .Sample(TimeSpan.FromMilliseconds(100)) //进度改变时，每 100 毫秒更新一次进度
                .Select(h => h.EventArgs)
                .Where(arg => arg != null)
                .ObserveOn(this);

            data_downloaded = Observable.FromEventPattern<DownloadDataCompletedEventArgs>(client, "DownloadDataCompleted")
                .Select(h => h.EventArgs)
                .Where(arg => arg.Result != null && arg.Result.Length > 0)
                .ObserveOn(ThreadPoolScheduler.Instance);

            url_keydown = Observable.FromEventPattern<KeyEventArgs>(txt_url, "KeyDown")
                .Throttle(TimeSpan.FromMilliseconds(500)) //过滤 500 毫秒内的按键抖动
                .Select(h => h.EventArgs)
                //过滤 正在下载、按下的不是Enter、没有输入下载符 的情况
                .Where(arg => !client.IsBusy && arg.KeyCode == Keys.Enter && !string.IsNullOrWhiteSpace(txt_url.Text));

            #endregion

            #region 订阅事件

            mouse_move_dis = mouse_move.Select(arg => arg.Location)
                .Subscribe(point => WriteLine($"X: {point.X}  Y: {point.Y}  Time: {DateTime.Now.ToString("hh:mm:ss ffff")}"));

            progress_change_dis = progress_change.Subscribe(
                arg => UpdateProgress(arg.BytesReceived, arg.TotalBytesToReceive),
                err => UpdateProgress("下载失败"));

            data_download_dis = data_downloaded.Subscribe(resp =>
            {
                var filename = $"{AppDomain.CurrentDomain.BaseDirectory}/download{DateTime.Now.ToString("yyyyMMddhhmmssffff")}.rx";
                File.WriteAllBytes(filename, resp.Result);
                ShowMessage($"下载成功！文件位于\n{filename}");
            }, onError => ShowMessage("下载失败"));

            url_keydown_dis = url_keydown.Subscribe(onNext =>
            {
                client.DownloadDataAsync(new Uri(txt_url.Text));
                ProgressVisible(true);
            });

            #endregion
        }

        private void FrmSample_FormClosed(object sender, FormClosedEventArgs e)
        {
            //取消订阅
            url_keydown_dis?.Dispose();
            mouse_move_dis?.Dispose();
            progress_change_dis?.Dispose();
            data_download_dis?.Dispose();
            //取消异步下载
            client?.CancelAsync();
        }

        private void Btn_copy_Click(object sender, EventArgs e)
        {
            var copys = new StringBuilder();
            var select_items = new List<object>();
            foreach (var item in lb_out.SelectedItems) select_items.Add(item);

            select_items.ToObservable()
                .Select(x => Convert.ToString(x))
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .Subscribe(item => copys.AppendLine(item), () => Clipboard.SetDataObject(copys.ToString()));
        }

        private void WriteLine(object obj)
        {
            RunOnUI(() =>
            {
                lb_out.Items.Add(obj);
                lb_out.SelectedIndex = lb_out.Items.Count - 1;
            });
        }

        private void ProgressVisible(bool visible)
        {
            RunOnUI(() => lbl_progress.Visible = pb_down.Visible = visible);
        }

        private void UpdateProgress(long value, long count)
        {
            RunOnUI(() =>
            {
                lbl_progress.Text = $"{value} Bytes / {count} Bytes";
                pb_down.Maximum = (int)count;
                pb_down.Value = (int)value;
            });
        }

        private void UpdateProgress(string text)
        {
            RunOnUI(() =>
            {
                pb_down.Value = 0;
                lbl_progress.Text = text;
            });
        }
    }
}
